using System;
using System.IO;
using System.Collections.Generic;
using System.Text;

using LumiSoft.Net.Log;

namespace LumiSoft.Net.IO
{
    /// <summary>
    /// This delegate represents callback method for BeginReadLine.
    /// </summary>
    /// <param name="e">Method data.</param>
    public delegate void ReadLineCallback(ReadLine_EventArgs e);

    /// <summary>
    /// This delegate represents callback method for BeginReadToEnd,BeginReadHeader,BeginReadPeriodTerminated.
    /// </summary>
    /// <param name="e">Method data.</param>
    public delegate void ReadToStreamCallback(ReadToStream_EventArgs e);

    /// <summary>
    /// This delegate represents callback method for BeginWrite.
    /// </summary>
    /// <param name="e">Method data.</param>
    public delegate void WriteCallback(Write_EventArgs e);

    /// <summary>
    /// This delegate represents callback method for BeginWrite,BeginWritePeriodTerminated.
    /// </summary>
    /// <param name="e">Method data.</param>
    public delegate void WriteStreamCallback(WriteStream_EventArgs e);

    /// <summary>
    /// Stream wrapper class, provides many usefull read and write methods for stream.
    /// </summary>
    [Obsolete("Use SmartStream instead.")]
    public class StreamHelper
    {
        #region class _ToStreamReader

        /// <summary>
        /// Asynchronous to stream reader implementation.
        /// </summary>
        private class _ToStreamReader
        {
            private StreamHelper         m_pStreamHelper      = null;
            private Stream               m_pStoreStream       = null;
            private BufferedStream       m_pBufferedStream    = null;
            private int                  m_MaxSize            = 32000;
            private SizeExceededAction   m_ExceededAction     = SizeExceededAction.ThrowException;
            private ReadToStreamCallback m_pCallback          = null;
            private object               m_pTag               = null;
            private byte[]               m_pLineBuffer        = null;
            private byte[]               m_pBuffer            = null;
            private int                  m_CountInBuffer      = 0;
            private int                  m_TotalReadedCount   = 0;
            private bool                 m_IsLineSizeExceeded = false;
            private int                  m_CountToRead        = 0;

            /// <summary>
            /// Default constructor.
            /// </summary>
            /// <param name="streamHelper">Reference to StreamHelper.</param>
            /// <param name="storeStream">Stream where to store readed data.</param>
            /// <param name="maxSize">Maximum number of bytes to read.</param>
            /// <param name="exceededAction">Specifies how this method behaves when maximum size exceeded.</param>
            /// <param name="callback">Callback what will be called if asynchronous reading compltes.</param>
            /// <param name="tag">User data.</param>
            public _ToStreamReader(StreamHelper streamHelper,Stream storeStream,int maxSize,SizeExceededAction exceededAction,ReadToStreamCallback callback,object tag)
            {
                m_pStreamHelper   = streamHelper;
                m_pStoreStream    = storeStream;
                m_pBufferedStream = new BufferedStream(m_pStoreStream,32000);
                m_MaxSize         = maxSize;
                m_ExceededAction  = exceededAction;
                m_pCallback       = callback;
                m_pTag            = tag;
                m_pBuffer         = new byte[32000];
                m_pLineBuffer     = new byte[4096];
            }


            #region method BeginRead

            /// <summary>
            /// Starts reading specified amount of data.
            /// </summary>
            /// <param name="count">Number of bytes to read from source stream and store to store stream.</param>
            public void BeginRead(int count)
            {
                m_CountToRead = count;

                DoRead();
            }

            #endregion

            #region method DoRead

            /// <summary>
            /// Processes all buffer data and gets new buffer if active buffer consumed.
            /// </summary>
            private void DoRead()
            {
                /* Note: We do own read buffering here, because we cand do buffering here even if
                 *       buffering not enabled for StreamHelper. All this because we read all data anyway. 
                 *       But we need to use StreamHelper read buffer first if there is any data in that buffer.
                */

                try{
                    // We have data in buffer, consume it.
                    if(m_CountInBuffer > 0){
                        m_TotalReadedCount += m_CountInBuffer;

                        // Write readed data to store stream.
                        m_pStoreStream.Write(m_pBuffer,0,m_CountInBuffer);                        
                        m_CountInBuffer = 0;
                    }

                    // We have readed all data that was requested.
                    if(m_TotalReadedCount == m_CountToRead){
                        OnRead_Completed(null);
                    }
                    else{                 
                        // There is some data in StreamHelper read buffer, we need to consume it first.
                        if(m_pStreamHelper.m_ReadBufferOffset < m_pStreamHelper.m_ReadBufferEndPos){
                            int countReadedFromBuffer = Math.Min(m_pStreamHelper.m_ReadBufferEndPos - m_pStreamHelper.m_ReadBufferOffset,m_CountToRead - m_TotalReadedCount);
                            Array.Copy(m_pStreamHelper.m_pReadBuffer,m_pStreamHelper.m_ReadBufferOffset,m_pBuffer,0,countReadedFromBuffer);
                            m_pStreamHelper.m_ReadBufferOffset += countReadedFromBuffer;
                        }
                        // Start reading new (local)buffer data block.
                        else{
                            m_pStreamHelper.Stream.BeginRead(
                                m_pBuffer,
                                0,
                                Math.Min(m_pBuffer.Length,m_CountToRead - m_TotalReadedCount),
                                new AsyncCallback(this.OnRead_ReadBuffer_Completed),
                                null
                            );
                        }
                    }
                }
                catch(Exception x){
                    OnRead_Completed(x);
                }
            }

            #endregion

            #region method OnRead_ReadBuffer_Completed

            /// <summary>
            /// Is called when asynchrounous data buffer block reading has completed.
            /// </summary>
            /// <param name="result"></param>
            private void OnRead_ReadBuffer_Completed(IAsyncResult result)
            {
                try{
                    m_CountInBuffer = m_pStreamHelper.Stream.EndRead(result);

                    // We reached end of stream, no more data.
                    if(m_CountInBuffer == 0){
                        OnRead_Completed(new IncompleteDataException());
                    }
                    else{
                        // Continue reading.
                        DoRead();
                    }
                }
                catch(Exception x){
                    OnRead_Completed(x);
                }
            }

            #endregion

            #region method OnRead_Completed

            /// <summary>
            /// Is called when ReadHeader has completed.
            /// </summary>
            /// <param name="x">Exception happened during read or null if operation was successfull.</param>
            private void OnRead_Completed(Exception x)
            {
                // Release read lock.
                m_pStreamHelper.m_IsReadActive = false;

                // Log
                if(m_pStreamHelper.Logger != null){
                    m_pStreamHelper.Logger.AddRead(m_TotalReadedCount,null);
                }
                
                if(m_pCallback != null){
                    m_pCallback(new ReadToStream_EventArgs(x,m_pStoreStream,m_TotalReadedCount,m_pTag));
                }
            }

            #endregion


            #region method BeginReadPeriodTerminated

            /// <summary>
            /// Starts reading period terminated data.
            /// </summary>
            public void BeginReadPeriodTerminated()
            {
                try{
                    m_pStreamHelper.BeginReadLineInternal(m_pLineBuffer,m_ExceededAction,null,new ReadLineCallback(this.OnReadPeriodTerminated_ReadLine_Completed),false,false);
                }
                catch(Exception x){
                    ReadPeriodTerminatedCompleted(x);
                }
            }

            #endregion

            #region method OnReadPeriodTerminated_ReadLine_Completed

            /// <summary>
            /// Is called when asynchrounous line reading has completed.
            /// </summary>
            /// <param name="e">Callback data.</param>
            private void OnReadPeriodTerminated_ReadLine_Completed(ReadLine_EventArgs e)
            {
                try{
                    m_TotalReadedCount += e.ReadedCount;

                    // We got error.
                    if(e.Exception != null){ 
                        try{
                            m_pBufferedStream.Flush();
                            m_pStoreStream.Flush();
                        }
                        catch{
                            // Just skip excpetions here, otherwise we may hide original exception, 
                            // if exceptions is thrown by flush.
                        }

                        // Maximum line size exceeded, but junk data wanted.
                        if(m_ExceededAction == SizeExceededAction.JunkAndThrowException && e.Exception is LineSizeExceededException){
                            m_IsLineSizeExceeded = true;

                            // Start reading next data buffer block.
                            m_pStreamHelper.BeginReadLineInternal(m_pLineBuffer,m_ExceededAction,null,new ReadLineCallback(this.OnReadPeriodTerminated_ReadLine_Completed),false,false);
                        }
                        // Unknown exception or ThrowException, so we are done.
                        else{
                            ReadPeriodTerminatedCompleted(e.Exception);
                        }
                    }
                    // We reached end of stream before got period terminator.
                    else if(e.ReadedCount == 0){
                        ReadPeriodTerminatedCompleted(new IncompleteDataException("Source stream was reached end of stream and data is not period terminated !"));                        
                    }
                    // We got terminator, so we are done now.
                    else if(e.Count == 1 && e.LineBuffer[0] == '.'){
                        m_pBufferedStream.Flush();
                        m_pStoreStream.Flush();

                        // LineSizeExceeded.
                        if(m_IsLineSizeExceeded){
                            ReadPeriodTerminatedCompleted(new LineSizeExceededException());
                        }
                        // DataSizeExceeded.
                        else if(m_TotalReadedCount > m_MaxSize){
                            ReadPeriodTerminatedCompleted(new DataSizeExceededException());
                        }
                        // Completed successfuly.
                        else{
                            ReadPeriodTerminatedCompleted(null);
                        }         
                    }
                    // Just append line to store stream and get next line.
                    else{
                        // Maximum allowed data size exceeded.
                        if(m_TotalReadedCount > m_MaxSize){
                            if(m_ExceededAction == SizeExceededAction.ThrowException){
                                ReadPeriodTerminatedCompleted(new DataSizeExceededException());
                                return;
                            }
                            // Junk data.
                            //else{
                            //}
                        }
                        else{
                            // If line starts with period, first period is removed.
                            if(e.LineBuffer[0] == '.'){
                                m_pBufferedStream.Write(e.LineBuffer,1,e.Count - 1);
                            }
                            // Normal line.
                            else{
                                m_pBufferedStream.Write(e.LineBuffer,0,e.Count);
                            }
                            // Add line break.
                            m_pBufferedStream.Write(m_pStreamHelper.m_LineBreak,0,m_pStreamHelper.m_LineBreak.Length);
                        }
                        
                        // Start getting new data buffer block.   
                        m_pStreamHelper.BeginReadLineInternal(m_pLineBuffer,m_ExceededAction,null,new ReadLineCallback(this.OnReadPeriodTerminated_ReadLine_Completed),false,false);
                    }
                }
                catch(Exception x){
                    ReadPeriodTerminatedCompleted(x);
                }
            }

            #endregion

            #region method ReadPeriodTerminatedCompleted

            /// <summary>
            /// Is called when ReadPeriodTerminated has completd.
            /// </summary>
            /// <param name="x">Exeption happened or null if operation completed successfuly.</param>
            private void ReadPeriodTerminatedCompleted(Exception x)
            {
                // Release read lock.
                m_pStreamHelper.m_IsReadActive = false;

                // Log
                if(m_pStreamHelper.Logger != null){
                    m_pStreamHelper.Logger.AddRead(m_TotalReadedCount,null);
                }

                if(m_pCallback != null){
                    m_pCallback(new ReadToStream_EventArgs(x,m_pStoreStream,m_TotalReadedCount,m_pTag));
                }
            }

            #endregion


            #region method BeginReadHeader

            /// <summary>
            /// Starts reading header data.
            /// </summary>
            public void BeginReadHeader()
            {
                try{
                    m_pStreamHelper.BeginReadLineInternal(m_pLineBuffer,m_ExceededAction,null,new ReadLineCallback(this.OnReadHeader_ReadLine_Completed),false,false);
                }
                catch(Exception x){
                    ReadHeaderCompleted(x);
                }
            }

            #endregion
                       
            #region method OnReadHeader_ReadLine_Completed

            /// <summary>
            /// Is called when asynchrounous line reading has completed.
            /// </summary>
            /// <param name="e">Callback data.</param>
            private void OnReadHeader_ReadLine_Completed(ReadLine_EventArgs e)
            {
                try{
                    m_TotalReadedCount += e.ReadedCount;

                    // We got error.
                    if(e.Exception != null){ 
                        try{
                            m_pBufferedStream.Flush();
                            m_pStoreStream.Flush();
                        }
                        catch{
                            // Just skip excpetions here, otherwise we may hide original exception, 
                            // if exceptions is thrown by flush.
                        }

                        // Maximum line size exceeded, but junk data wanted.
                        if(m_ExceededAction == SizeExceededAction.JunkAndThrowException && e.Exception is LineSizeExceededException){
                            m_IsLineSizeExceeded = true;

                            // Start reading next data buffer block.
                            m_pStreamHelper.BeginReadLineInternal(m_pLineBuffer,m_ExceededAction,null,new ReadLineCallback(this.OnReadHeader_ReadLine_Completed),false,false);
                        }
                        // Unknown exception or ThrowException, so we are done.
                        else{
                            ReadHeaderCompleted(e.Exception);
                        }
                    }
                    // We got terminator, so we are done now.
                    else if(e.Count == 0 || e.ReadedCount == 0){
                        m_pBufferedStream.Flush();
                        m_pStoreStream.Flush();
                        
                        // LineSizeExceeded.
                        if(m_IsLineSizeExceeded){
                            ReadHeaderCompleted(new LineSizeExceededException());
                        }
                        // DataSizeExceeded.
                        else if(m_TotalReadedCount > m_MaxSize){
                            ReadHeaderCompleted(new DataSizeExceededException());
                        }
                        // Completed successfuly.
                        else{
                            ReadHeaderCompleted(null);
                        }
                    }
                    // Just append line to store stream and get next line.
                    else{
                        // Maximum allowed data size exceeded.
                        if(m_TotalReadedCount > m_MaxSize){
                            if(m_ExceededAction == SizeExceededAction.ThrowException){
                                ReadHeaderCompleted(new DataSizeExceededException());
                                return;
                            }
                            // Junk data.
                            //else{
                            //}
                        }
                        else{
                            m_pBufferedStream.Write(e.LineBuffer,0,e.Count);
                            m_pBufferedStream.Write(m_pStreamHelper.m_LineBreak,0,m_pStreamHelper.m_LineBreak.Length);
                        }

                        // Start reading new line.
                        m_pStreamHelper.BeginReadLineInternal(m_pLineBuffer,m_ExceededAction,null,new ReadLineCallback(this.OnReadHeader_ReadLine_Completed),false,false);
                    }
                }
                catch(Exception x){
                    ReadHeaderCompleted(x);
                }
            }

            #endregion

            #region method ReadHeaderCompleted

            /// <summary>
            /// Is called when ReadHeader has completed.
            /// </summary>
            /// <param name="x">Exception happened during read or null if operation was successfull.</param>
            private void ReadHeaderCompleted(Exception x)
            {
                // Release read lock.
                m_pStreamHelper.m_IsReadActive = false;

                // Log
                if(m_pStreamHelper.Logger != null){
                    m_pStreamHelper.Logger.AddRead(m_TotalReadedCount,null);
                }
                
                if(m_pCallback != null){
                    m_pCallback(new ReadToStream_EventArgs(x,m_pStoreStream,m_TotalReadedCount,m_pTag));
                }
            }

            #endregion


            #region method BeginReadAll

            /// <summary>
            /// Starts reading all source stream data.
            /// </summary>
            public void BeginReadAll()
            {
                DoReadAll();
            }

            #endregion

            #region method DoReadAll

            /// <summary>
            /// Processes all buffer data and gets new buffer if active buffer consumed.
            /// </summary>
            private void DoReadAll()
            {
                /* Note: We do own read buffering here, because we cand do buffering here even if
                 *       buffering not enabled for StreamHelper. All this because we read all data anyway. 
                 *       But we need to use StreamHelper read buffer first if there is any data in that buffer.
                */

                try{                    
                    // We have data in buffer, consume it.
                    if(m_CountInBuffer > 0){
                        m_TotalReadedCount += m_CountInBuffer;

                        // Maximum allowed data size exceeded.
                        if(m_TotalReadedCount > m_MaxSize){
                            if(m_ExceededAction == SizeExceededAction.ThrowException){
                                ReadAllCompleted(new DataSizeExceededException());
                                return;
                            }
                            // Junk data.
                            //else{
                            //}
                        }
                        // Store readed data store stream.
                        else{
                            m_pStoreStream.Write(m_pBuffer,0,m_CountInBuffer);
                        }
                        m_CountInBuffer = 0;
                    }
                                     
                    // There is some data in StreamHelper read buffer, we need to consume it first.
                    if(m_pStreamHelper.m_ReadBufferOffset < m_pStreamHelper.m_ReadBufferEndPos){
                        Array.Copy(m_pStreamHelper.m_pReadBuffer,m_pStreamHelper.m_ReadBufferOffset,m_pBuffer,0,m_pStreamHelper.m_ReadBufferEndPos - m_pStreamHelper.m_ReadBufferOffset);
                        m_pStreamHelper.m_ReadBufferOffset = 0;
                        m_pStreamHelper.m_ReadBufferEndPos = 0;
                    }
                    // Start reading new (local)buffer data block.
                    else{
                        m_pStreamHelper.Stream.BeginRead(m_pBuffer,0,m_pBuffer.Length,new AsyncCallback(this.OnReadAll_ReadBuffer_Completed),null);
                    }
                }
                catch(Exception x){
                    ReadAllCompleted(x);
                }
            }

            #endregion

            #region method OnReadAll_ReadBuffer_Completed

            /// <summary>
            /// Is called when asynchrounous data buffer block reading has completed.
            /// </summary>
            /// <param name="result"></param>
            private void OnReadAll_ReadBuffer_Completed(IAsyncResult result)
            {
                try{
                    m_CountInBuffer = m_pStreamHelper.Stream.EndRead(result);
                    // We reached end of stream, no more data.
                    if(m_CountInBuffer == 0){
                        // Maximum allowed data size exceeded.
                        if(m_TotalReadedCount > m_MaxSize){
                            ReadAllCompleted(new DataSizeExceededException());
                        }
                        // ReadToEnd completed successfuly.
                        else{
                            ReadAllCompleted(null);
                        }
                    }
                    else{
                        // Continue reading.
                        DoReadAll();
                    }
                }
                catch(Exception x){
                    ReadAllCompleted(x);
                }
            }

            #endregion

            #region method ReadAllCompleted

            /// <summary>
            /// Is called when ReadToEnd has completed.
            /// </summary>
            /// <param name="x">Exception happened during read or null if operation was successfull.</param>
            private void ReadAllCompleted(Exception x)
            {
                // Release read lock.
                m_pStreamHelper.m_IsReadActive = false;
                
                // Log
                if(m_pStreamHelper.Logger != null){
                    m_pStreamHelper.Logger.AddRead(m_TotalReadedCount,null);
                }

                if(m_pCallback != null){
                    m_pCallback(new ReadToStream_EventArgs(x,m_pStoreStream,m_TotalReadedCount,m_pTag));
                }
            }

            #endregion
        }

        #endregion
                
        private Stream      m_pStream          = null;
        private int         m_MaxLineSize      = 4096;
        private bool        m_IsReadBuffered   = false;
        private byte[]      m_pReadBuffer      = null;
        private int         m_ReadBufferOffset = 0;
        private int         m_ReadBufferEndPos = 0;
        private byte[]      m_pLineBuffer      = null;
        private byte[]      m_LineBreak        = new byte[]{(byte)'\r',(byte)'\n'};
        private bool        m_IsReadActive     = false;
        private bool        m_IsWriteActive    = false;
        private Logger      m_pLogger          = null;
        // BeginReadLine
        private byte[]             m_pRLine_LineBuffer      = null;
        private byte[]             m_pRLine_ByteBuffer      = null;        
        private int                m_RLine_LineBufferSize   = 0;
        private int                m_RLine_LineBufferOffset = 0;
        private int                m_RLine_TotalReadedCount = 0;
        private SizeExceededAction m_RLine_ExceedAction     = SizeExceededAction.ThrowException;
        private bool               m_RLine_UnlockRead       = true;
        private bool               m_RLine_Log              = false;
        private ReadLineCallback   m_pRLine_Callback        = null;
        private object             m_pRLine_Tag             = null;
        private ReadLine_EventArgs m_pRLine_EventArgs       = null;
        // BeginWrite
        private byte[]              m_pBeginWrite_Buffer   = null;
        private Stream              m_pBeginWrite_Stream   = null;
        private int                 m_BeginWrite_Count     = 0;
        private WriteStreamCallback m_pBeginWrite_Callback = null;
        private int                 m_BeginWrite_Readed    = 0;
        // BeginWriteAll
        private byte[]              m_pBeginWriteAll_Buffer   = null;
        private Stream              m_pBeginWriteAll_Stream   = null;
        private int                 m_BeginWriteAll_MaxSize   = 0;
        private WriteStreamCallback m_pBeginWriteAll_Callback = null;
        private int                 m_BeginWriteAll_Readed    = 0;
        // BeginWritePeriodTerminated
        private Stream              m_pBeginWritePeriodTerminated_Stream         = null;
        private int                 m_BeginWritePeriodTerminated_MaxSize         = 0;
        private WriteStreamCallback m_pBeginWritePeriodTerminated_Callback       = null;
        private BufferedStream      m_pBeginWritePeriodTerminated_BufferedStream = null;
        private int                 m_BeginWritePeriodTerminated_Readed          = 0;
        private int                 m_BeginWritePeriodTerminated_Written         = 0;

        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="stream">Source stream.</param>
        public StreamHelper(Stream stream) : this(stream,4096,true)
        {
        }

        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="stream">Source stream.</param>
        /// <param name="maxLineSize">Specifies maximum line size in bytes.</param>
        /// <param name="bufferRead">Specifies if source stream reads are buffered..</param>
        public StreamHelper(Stream stream,int maxLineSize,bool bufferRead)
        {
            if(stream == null){
                throw new ArgumentNullException("stream");
            }
            if(maxLineSize < 1 || maxLineSize > 32000){
                throw new ArgumentException("Parameter maxLineSize value must be >= 1 and <= 32000 !");
            }

            m_IsReadBuffered = bufferRead;
            if(m_IsReadBuffered){
                m_pReadBuffer = new byte[32000];
            }

            m_pStream = stream;
            m_pLineBuffer = new byte[maxLineSize];
            m_MaxLineSize = maxLineSize;

            m_pRLine_EventArgs = new ReadLine_EventArgs();
            m_pRLine_ByteBuffer = new byte[1];
        }


        #region method ReadByte

        /// <summary>
        /// Reades byte from source stream. Returns -1 if end of stream reached and no more data.
        /// </summary>
        /// <returns>Returns readed byte or -1 if end of stream reached.</returns>
        public int ReadByte()
        {
            if(m_IsReadBuffered){
                if(m_ReadBufferOffset >= m_ReadBufferEndPos){
                    m_ReadBufferEndPos = m_pStream.Read(m_pReadBuffer,0,32000);
                    m_ReadBufferOffset = 0;

                    // We reached end of stream.
                    if(m_ReadBufferEndPos == 0){
                        return -1;
                    }
                }

                m_ReadBufferOffset++;
                return m_pReadBuffer[m_ReadBufferOffset - 1];                
            }
            else{
                return m_pStream.ReadByte();
            }
        }

        #endregion

        #region method BeginRead

        /// <summary>
        /// Starts reading specified amount data and storing to the specified store stream.
        /// </summary>
        /// <param name="storeStream">Stream where to store readed data.</param>
        /// <param name="count">Number of bytes to read from source stream and write to store stream.</param>
        /// <param name="callback">Callback to be called if asynchronous reading completes.</param>
        /// <param name="tag">User data.</param>
        /// <exception cref="ArgumentNullException">Raised when <b>storeStream</b> is null.</exception>
        /// <exception cref="ArgumentException">Raised when <b>count</b> less than 1.</exception>
        public void BeginRead(Stream storeStream,int count,ReadToStreamCallback callback,object tag)
        {
            if(storeStream == null){
                throw new ArgumentNullException("storeStream");
            }
            if(count < 1){
                throw new ArgumentException("Parameter count value must be >= 1 !");
            }
            lock(this){
                if(m_IsReadActive){
                    throw new InvalidOperationException("There is pending read operation, multiple read operations not allowed !");
                }
                m_IsReadActive = true;
            }

            _ToStreamReader reader = new _ToStreamReader(this,storeStream,0,SizeExceededAction.ThrowException,callback,tag);
            reader.BeginRead(count);
        }

        #endregion

        #region method Read

        /// <summary>
        /// Reads specified amount of data from source stream and stores to specified store stream.
        /// </summary>
        /// <param name="storeStream">Stream where to store readed data.</param>
        /// <param name="count">Number of bytes to read from source stream and write to store stream.</param>
        /// <exception cref="ArgumentNullException">Raised when <b>storeStream</b> is null.</exception>
        /// <exception cref="ArgumentException">Raised when <b>count</b> less than 1.</exception>
        /// <exception cref="InvalidOperationException">Raised when there already is pending read operation.</exception>
        /// <exception cref="IncompleteDataException">Raised source stream has reached end of stream and doesn't have so much data as specified by <b>count</b> argument.</exception>
        public void Read(Stream storeStream,int count)
        {            
            if(storeStream == null){
                throw new ArgumentNullException("storeStream");
            }
            if(count < 1){
                throw new ArgumentException("Parameter count value must be >= 1 !");
            }
            lock(this){
                if(m_IsReadActive){
                    throw new InvalidOperationException("There is pending read operation, multiple read operations not allowed !");
                }
                m_IsReadActive = true;
            }

            try{
                byte[] buffer           = new byte[32000];
                int    totalReadedCount = 0;
                int    readedCount      = 0;
                while(totalReadedCount < count){
                    // We have data in read buffer, we must consume it first !
                    if(m_ReadBufferOffset < m_ReadBufferEndPos){
                        int countReadedFromBuffer = Math.Min(m_ReadBufferEndPos - m_ReadBufferOffset,count - totalReadedCount);
                        Array.Copy(m_pReadBuffer,m_ReadBufferOffset,buffer,0,countReadedFromBuffer);
                        m_ReadBufferOffset += countReadedFromBuffer;
                    }
                    // Just get read next data block.
                    else{
                        readedCount = m_pStream.Read(buffer,0,Math.Min(buffer.Length,count - totalReadedCount));
                    }

                    // We have reached end of stream, no more data.
                    if(readedCount == 0){
                        throw new IncompleteDataException("Underlaying stream don't have so much data than requested, end of stream reached !");                        
                    }
                    totalReadedCount += readedCount;

                    // Write readed data to store stream.
                    storeStream.Write(buffer,0,readedCount);
                }

                // Log
                if(this.Logger != null){
                    this.Logger.AddRead(totalReadedCount,null);
                }
            }
            finally{
                m_IsReadActive = false;
            }
        }

        #endregion

        #region method BeginReadLine

        /// <summary>
        /// Starts reading line from source stream.
        /// </summary>
        /// <param name="buffer">Buffer where to store line data.</param>
        /// <param name="tag">User data.</param>
        /// <param name="callback">Callback to be called whan asynchronous operation completes.</param>
        /// <exception cref="ArgumentNullException">Raised when <b>buffer</b> is null.</exception>
        /// <exception cref="InvalidOperationException">Raised when there already is pending read operation.</exception>
        public void BeginReadLine(byte[] buffer,object tag,ReadLineCallback callback)
        {
            BeginReadLine(buffer,SizeExceededAction.ThrowException,tag,callback);
        }

        /// <summary>
        /// Starts reading line from source stream.
        /// </summary>
        /// <param name="buffer">Buffer where to store line data.</param>
        /// <param name="exceededAction">Specifies how this method behaves when maximum line size exceeded.</param>
        /// <param name="tag">User data.</param>
        /// <param name="callback">Callback to be called whan asynchronous operation completes.</param>
        /// <exception cref="ArgumentNullException">Raised when <b>buffer</b> is null.</exception>
        /// <exception cref="InvalidOperationException">Raised when there already is pending read operation.</exception>
        public void BeginReadLine(byte[] buffer,SizeExceededAction exceededAction,object tag,ReadLineCallback callback)
        {
            if(buffer == null){
                throw new ArgumentNullException("buffer");
            }
            lock(this){
                if(m_IsReadActive){
                    throw new InvalidOperationException("There is pending read operation, multiple read operations not allowed !");
                }
                m_IsReadActive = false;
            }

            BeginReadLineInternal(buffer,exceededAction,tag,callback,true,true);
        }

        /// <summary>
        /// Starts reading line from source stream. This method does not do any checks and read locks.
        /// </summary>
        /// <param name="buffer">Buffer where to store line data.</param>
        /// <param name="exceededAction">Specifies how this method behaves when maximum line size exceeded.</param>
        /// <param name="tag">User data.</param>
        /// <param name="callback">Callback to be called whan asynchronous operation completes.</param>
        /// <param name="unlockRead">Specifies if read lock is released.</param>
        /// <param name="log">User data.</param>
        private void BeginReadLineInternal(byte[] buffer,SizeExceededAction exceededAction,object tag,ReadLineCallback callback,bool unlockRead,bool log)
        {            
            m_pRLine_LineBuffer      = buffer;
            m_RLine_ExceedAction     = exceededAction;
            m_pRLine_Tag             = tag;
            m_pRLine_Callback        = callback;
            m_RLine_LineBufferOffset = 0;
            m_RLine_TotalReadedCount = 0;                
            m_RLine_LineBufferSize   = buffer.Length;
            m_RLine_UnlockRead       = unlockRead;
            m_RLine_Log              = log;

            if(this.IsReadBuffered){
                DoReadLine_Buffered();
            }
            else{
                m_pStream.BeginRead(m_pRLine_ByteBuffer,0,1,new AsyncCallback(this.OnReadByte_Completed),null);
            }
        }

        #endregion

        #region method ReadLine
               
        /// <summary>
        /// Reads line from source stream and stores to specified buffer. This method accepts LF or CRLF lines.
        /// </summary>
        /// <param name="buffer">Buffer where to store line data.</param>
        /// <returns>Returns number of bytes stored to buffer, returns -1 if end of stream reached and no more data.</returns>
        /// <exception cref="ArgumentNullException">Raised when <b>buffer</b> is null.</exception>
        /// <exception cref="InvalidOperationException">Raised when there already is pending read operation.</exception>
        /// <exception cref="LineSizeExceededException">Raised when maximum allowed line size has exceeded.</exception>
        public int ReadLine(byte[] buffer)
        {
            return ReadLine(buffer,SizeExceededAction.ThrowException);
        }

        /// <summary>
        /// Reads line from source stream and stores to specified buffer. This method accepts LF or CRLF lines.
        /// </summary>
        /// <param name="buffer">Buffer where to store line data.</param>
        /// <param name="exceededAction">Specifies how this method behaves when maximum line size exceeded.</param>
        /// <returns>Returns number of bytes stored to buffer, returns -1 if end of stream reached and no more data.</returns>
        /// <exception cref="ArgumentNullException">Raised when <b>buffer</b> is null.</exception>
        /// <exception cref="InvalidOperationException">Raised when there already is pending read operation.</exception>
        /// <exception cref="LineSizeExceededException">Raised when maximum allowed line size has exceeded.</exception>
        public int ReadLine(byte[] buffer,SizeExceededAction exceededAction)
        {
            if(buffer == null){
                throw new ArgumentNullException("buffer");
            }
            lock(this){
                if(m_IsReadActive){
                    throw new InvalidOperationException("There is pending read operation, multiple read operations not allowed !");
                }
                else{
                    m_IsReadActive = true;
                }
            }

            try{
                int readedCount = 0;
                return ReadLineInternal(buffer,exceededAction,out readedCount,true);
            }
            finally{
                m_IsReadActive = false;
            }
        }

        /// <summary>
        /// Reads line from source stream.
        /// </summary>
        /// <param name="encoding">Encoding to use to decode line.</param>
        /// <returns>Returns readed line with specified encoding or null if end of stream reached and no more data.</returns>
        /// <exception cref="ArgumentNullException">Raised when <b>encoding</b> is null.</exception>
        /// <exception cref="InvalidOperationException">Raised when there already is pending read operation.</exception>
        /// <exception cref="LineSizeExceededException">Raised when maximum allowed line size has exceeded.</exception>
        public string ReadLine(Encoding encoding)
        {
            return ReadLine(encoding,SizeExceededAction.ThrowException);
        }

        /// <summary>
        /// Reads line from source stream.
        /// </summary>
        /// <param name="encoding">Encoding to use to decode line.</param>
        /// <param name="exceededAction">Specifies how this method behaves when maximum line size exceeded.</param>
        /// <returns>Returns readed line with specified encoding or null if end of stream reached and no more data.</returns>
        /// <exception cref="ArgumentNullException">Raised when <b>encoding</b> is null.</exception>
        /// <exception cref="InvalidOperationException">Raised when there already is pending read operation.</exception>
        /// <exception cref="LineSizeExceededException">Raised when maximum allowed line size has exceeded.</exception>
        public string ReadLine(Encoding encoding,SizeExceededAction exceededAction)
        {
            if(encoding == null){
                throw new ArgumentNullException("encoding");
            }

            int readedCount = ReadLine(m_pLineBuffer,exceededAction);
            if(readedCount == -1){
                return null;                
            }
            else{
                return encoding.GetString(m_pLineBuffer,0,readedCount);
            }
        }
                
        /// <summary>
        /// Reads line from source stream and stores to specified buffer. This method accepts LF or CRLF lines.
        /// </summary>
        /// <param name="buffer">Buffer where to store line data.</param>
        /// <param name="exceededAction">Specifies how this method behaves when maximum line size exceeded.</param>
        /// <param name="readedCount">Returns how many bytes this method actually readed form source stream.</param>
        /// <param name="log">Specifies if read line is logged.</param>
        /// <returns>Returns number of bytes stored to buffer, returns -1 if end of stream reached and no more data.</returns>
        /// <exception cref="InvalidOperationException">Raised when there already is pending read operation.</exception>
        /// <exception cref="LineSizeExceededException">Raised when maximum allowed line size has exceeded.</exception>
        public int ReadLineInternal(byte[] buffer,SizeExceededAction exceededAction,out int readedCount,bool log)
        {
                readedCount = 0;
            int bufferSize  = buffer.Length;            
            int posInBuffer = 0;
            int currentByte = 0;

            /* Because performance gain we need todo,2 while, buffered and non buffered read.
               Each if clause in this while adds about 5% cpu.
            */

            #region Buffered

            if(m_IsReadBuffered){
                while(true){
                    //--- Read byte -----------------------------------------------------
                    if(m_ReadBufferOffset >= m_ReadBufferEndPos){
                        m_ReadBufferEndPos = m_pStream.Read(m_pReadBuffer,0,32000);
                        m_ReadBufferOffset = 0;

                        // We reached end of stream.
                        if(m_ReadBufferEndPos == 0){
                            break;
                        }
                    }
                      
                    currentByte = m_pReadBuffer[m_ReadBufferOffset];
                    m_ReadBufferOffset++;
                    readedCount++;
                    //-------------------------------------------------------------------
                                         
                    // We have LF.
                    if(currentByte == '\n'){
                        break;
                    }
                    // We just skip all CR.
                    else if(currentByte == '\r'){
                    }
                    // Maximum allowed line size exceeded.
                    else if(readedCount > bufferSize){
                        if(exceededAction == SizeExceededAction.ThrowException){
                            throw new LineSizeExceededException();
                        }
                    }
                    // Store readed byte.
                    else{
                        buffer[posInBuffer] = (byte)currentByte;
                        posInBuffer++;
                    }                    
                }
            }

            #endregion

            #region No-buffered

            else{
                while(true){
                    // Read byte
                    currentByte = m_pStream.ReadByte();
                    // We reached end of stream, no more data.
                    if(currentByte == -1){
                        break;
                    }
                    readedCount++;
                    
                    // We have LF.
                    if(currentByte == '\n'){
                        break;
                    }
                    // We just skip all CR.
                    else if(currentByte == '\r'){
                    }
                    // Maximum allowed line size exceeded.
                    else if(readedCount > bufferSize){
                        if(exceededAction == SizeExceededAction.ThrowException){
                            throw new LineSizeExceededException();
                        }
                    }
                    // Store readed byte.
                    else{
                        buffer[posInBuffer] = (byte)currentByte;
                        posInBuffer++;
                    }
                }
            }

            #endregion
            
            // We are end of stream, no more data.
            if(readedCount == 0){
                return -1;
            }
            // Maximum allowed line size exceeded.
            if(readedCount > m_MaxLineSize){
                throw new LineSizeExceededException();
            }

            // Log
            if(log && this.Logger != null){
                this.Logger.AddRead(readedCount,Encoding.Default.GetString(buffer,0,readedCount));
            }

            return posInBuffer;
        }

        #endregion

        #region mehtod BeginReadAll

        /// <summary>
        /// Starts reading all source stream data.
        /// </summary>
        /// <param name="storeStream">Stream where to store data.</param>
        /// <param name="maxSize">Maximum muber of bytes to read.</param>
        /// <param name="exceededAction">Specifies how this method behaves when maximum size exceeded.</param>
        /// <param name="callback">Callback to be called if asynchronous reading completes.</param>
        /// <param name="tag">User data.</param>
        /// <exception cref="ArgumentNullException">Raised when <b>storeStream</b> is null.</exception>
        /// <exception cref="InvalidOperationException">Raised when there already is pending read operation.</exception>
        public void BeginReadAll(Stream storeStream,int maxSize,SizeExceededAction exceededAction,ReadToStreamCallback callback,object tag)
        {
            if(storeStream == null){
                throw new ArgumentNullException("storeStream");
            }
            lock(this){
                if(m_IsReadActive){
                    throw new InvalidOperationException("There is pending read operation, multiple read operations not allowed !");
                }
                m_IsReadActive = false;
            }

            _ToStreamReader reader = new _ToStreamReader(this,storeStream,maxSize,exceededAction,callback,tag);
            reader.BeginReadAll();
        }

        #endregion

        #region method ReadAll

        /// <summary>
        /// Reads all source stream data and stores to the specified store stream.
        /// </summary>
        /// <param name="storeStream">Stream where to store readed data.</param>
        /// <param name="maxSize">Maximum muber of bytes to read.</param>
        /// <returns>Returns number of bytes written to <b>storeStream</b>.</returns>
        /// <exception cref="ArgumentNullException">Raised when <b>storeStream</b> is null.</exception>
        /// <exception cref="ArgumentException">Raised when <b>maxSize</b> less than 1.</exception>
        /// <exception cref="InvalidOperationException">Raised when there already is pending read operation.</exception>
        /// <exception cref="DataSizeExceededException">Raised when maximum allowed data size has exceeded.</exception>
        public int ReadAll(Stream storeStream,int maxSize)
        {
            return ReadAll(storeStream,maxSize,SizeExceededAction.ThrowException);
        }

        /// <summary>
        /// Reads all source stream data and stores to the specified store stream.
        /// </summary>
        /// <param name="storeStream">Stream where to store readed data.</param>
        /// <param name="maxSize">Maximum muber of bytes to read.</param>
        /// <param name="exceededAction">Specifies how this method behaves when maximum size exceeded.</param>
        /// <returns>Returns number of bytes written to <b>storeStream</b>.</returns>
        /// <exception cref="ArgumentNullException">Raised when <b>storeStream</b> is null.</exception>
        /// <exception cref="ArgumentException">Raised when <b>maxSize</b> less than 1.</exception>
        /// <exception cref="InvalidOperationException">Raised when there already is pending read operation.</exception>
        /// <exception cref="DataSizeExceededException">Raised when maximum allowed data size has exceeded.</exception>
        public int ReadAll(Stream storeStream,int maxSize,SizeExceededAction exceededAction)
        {
            if(storeStream == null){
                throw new ArgumentNullException("storeStream");
            }
            if(maxSize < 1){
                throw new ArgumentException("Parameter maxSize value must be >= 1 !");
            }
            lock(this){
                if(m_IsReadActive){
                    throw new InvalidOperationException("There is pending read operation, multiple read operations not allowed !");
                }
                m_IsReadActive = true;
            }

            try{
                byte[] buffer           = new byte[32000];
                int    totalReadedCount = 0;
                int    readedCount      = 0;
                while(true){
                    // We have data in read buffer, we must consume it first !
                    if(m_ReadBufferOffset < m_ReadBufferEndPos){
                        Array.Copy(m_pLineBuffer,m_ReadBufferOffset,buffer,0,m_ReadBufferEndPos - m_ReadBufferOffset);
                        m_ReadBufferOffset = 0;
                        m_ReadBufferEndPos = 0;
                    }
                    // Just get read next data block.
                    else{
                        readedCount = m_pStream.Read(buffer,0,buffer.Length);
                    }

                    // End of stream reached, no more data.
                    if(readedCount == 0){
                        break;
                    }
                    totalReadedCount += readedCount;

                    // Maximum allowed data size exceeded.
                    if(totalReadedCount > maxSize){
                        if(exceededAction == SizeExceededAction.ThrowException){
                            throw new DataSizeExceededException();
                        }
                    }
                    else{
                        storeStream.Write(buffer,0,readedCount);
                    }                    
                }
                                
                // Maximum allowed data size exceeded, some data junked.
                if(totalReadedCount > maxSize){
                    throw new DataSizeExceededException();
                }

                // Log
                if(this.Logger != null){
                    this.Logger.AddRead(totalReadedCount,null);
                }

                return totalReadedCount;
            }
            finally{
                m_IsReadActive = false;
            }
        }

        #endregion

        #region method BeginReadHeader

        /// <summary>
        /// Starts reading header from source stream. Reads header data while gets blank line, what is 
        /// header terminator. For example this method can be used for reading mail,http,sip, ... headers.
        /// </summary>
        /// <param name="storeStream">Stream where to store data.</param>
        /// <param name="maxSize">Maximum muber of bytes to read.</param>
        /// <param name="exceededAction">Specifies how this method behaves when maximum size exceeded.</param>
        /// <param name="callback">Callback to be called if asynchronous reading completes.</param>
        /// <param name="tag">User data.</param>
        /// <exception cref="ArgumentNullException">Raised when <b>storeStream</b> is null.</exception>
        /// <exception cref="InvalidOperationException">Raised when there already is pending read operation.</exception>
        public void BeginReadHeader(Stream storeStream,int maxSize,SizeExceededAction exceededAction,ReadToStreamCallback callback,object tag)
        {
            if(storeStream == null){
                throw new ArgumentNullException("storeStream");
            }
            lock(this){
                if(m_IsReadActive){
                    throw new InvalidOperationException("There is pending read operation, multiple read operations not allowed !");
                }
                m_IsReadActive = false;
            }

            _ToStreamReader reader = new _ToStreamReader(this,storeStream,maxSize,exceededAction,callback,tag);
            reader.BeginReadHeader();
        }

        #endregion

        #region method ReadHeader

        /// <summary>
        /// Reads header from source stream and stores to the specified stream. Reads header data while 
        /// gets blank line, what is header terminator. For example this method can be used for reading 
        /// mail,http,sip, ... headers.
        /// </summary>
        /// <param name="storeStream">Stream where to store readed data.</param>
        /// <param name="maxSize">Maximum number of bytes to read.</param>
        /// <param name="exceededAction">Specifies how this method behaves when maximum line or data size exceeded.</param>
        /// <returns>Returns number of bytes written to <b>storeStream</b>.</returns>
        /// <exception cref="ArgumentNullException">Raised when <b>storeStream</b> is null.</exception>
        /// <exception cref="ArgumentException">Raised when <b>maxSize</b> less than 1.</exception>
        /// <exception cref="InvalidOperationException">Raised when there already is pending read operation.</exception>
        /// <exception cref="LineSizeExceededException">Raised when maximum allowed line size has exceeded.</exception>
        /// <exception cref="DataSizeExceededException">Raised when maximum allowed data size has exceeded.</exception>
        public int ReadHeader(Stream storeStream,int maxSize,SizeExceededAction exceededAction)
        {
            if(storeStream == null){
                throw new ArgumentNullException("storeStream");
            }
            lock(this){
                if(m_IsReadActive){
                    throw new InvalidOperationException("There is pending read operation, multiple read operations not allowed !");
                }
                else{
                    m_IsReadActive = true;
                }
            }

            try{
                BufferedStream bufferedStoreStream = new BufferedStream(storeStream,32000);
                bool lineSizeExceeded = false;
                int  totalReadedCount = 0;
                int  readedCount      = 0;
                int  rawReadedCount   = 0;
                while(true){
                    // Read line.
                    readedCount = this.ReadLineInternal(m_pLineBuffer,SizeExceededAction.ThrowException,out rawReadedCount,false);
                    
                    // We have reached end of stream, no more data.
                    if(rawReadedCount == 0){
                        break;
                    }
                    totalReadedCount += rawReadedCount;
                    
                    // We got header terminator.
                    if(readedCount == 0){
                        break;
                    }
                    else{
                        // Maximum allowed data size exceeded.
                        if(totalReadedCount > maxSize){
                            if(exceededAction == SizeExceededAction.ThrowException){
                                throw new DataSizeExceededException();
                            }
                        }
                        // Write readed bytes to store stream.
                        else{
                            bufferedStoreStream.Write(m_pLineBuffer,0,readedCount);
                            bufferedStoreStream.Write(m_LineBreak,0,m_LineBreak.Length);
                        }
                    }
                }
                bufferedStoreStream.Flush();
                                
                // Maximum allowed line size exceeded, some data is junked.
                if(lineSizeExceeded){
                    throw new LineSizeExceededException();
                }
                // Maximum allowed data size exceeded, some data is junked.
                if(totalReadedCount > maxSize){
                    throw new DataSizeExceededException();
                }

                // Log
                if(this.Logger != null){
                    this.Logger.AddRead(totalReadedCount,null);
                }

                return totalReadedCount;
            }
            finally{
                m_IsReadActive = false;
            }
        }

        #endregion

        #region method BeginReadPeriodTerminated

        /// <summary>
        /// Begins reading period terminated data from source stream. Reads data while gets single period on line,
        /// what is data terminator.
        /// </summary>
        /// <param name="storeStream">Stream where to store data.</param>
        /// <param name="maxSize">Maximum muber of bytes to read.</param>
        /// <param name="exceededAction">Specifies how this method behaves when maximum size exceeded.</param>
        /// <param name="callback">Callback to be called if asynchronous reading completes.</param>
        /// <param name="tag">User data.</param>
        /// <exception cref="ArgumentNullException">Raised when <b>storeStream</b> is null.</exception>
        /// <exception cref="InvalidOperationException">Raised when there already is pending read operation.</exception>
        public void BeginReadPeriodTerminated(Stream storeStream,int maxSize,SizeExceededAction exceededAction,ReadToStreamCallback callback,object tag)
        {
            if(storeStream == null){
                throw new ArgumentNullException("storeStream");
            }
            lock(this){
                if(m_IsReadActive){
                    throw new InvalidOperationException("There is pending read operation, multiple read operations not allowed !");
                }
                m_IsReadActive = false;
            }

            _ToStreamReader reader = new _ToStreamReader(this,storeStream,maxSize,exceededAction,callback,tag);
            reader.BeginReadPeriodTerminated();
        }

        #endregion

        #region method ReadPeriodTerminated

        /// <summary>
        /// Reads period terminated data from source stream. Reads data while gets single period on line,
        /// what is data terminator.
        /// </summary>
        /// <param name="storeStream">Stream where to store readed data.</param>
        /// <param name="maxSize">Maximum number of bytes to read.</param>
        /// <param name="exceededAction">Specifies how this method behaves when maximum size exceeded.</param>
        /// <returns>Returns number of bytes written to <b>storeStream</b>.</returns>
        /// <exception cref="ArgumentNullException">Raised when <b>storeStream</b> is null.</exception>
        /// <exception cref="ArgumentException">Raised when <b>maxSize</b> less than 1.</exception>
        /// <exception cref="InvalidOperationException">Raised when there already is pending read operation.</exception>
        /// <exception cref="LineSizeExceededException">Raised when maximum allowed line size has exceeded.</exception>
        /// <exception cref="DataSizeExceededException">Raised when maximum allowed data size has exceeded.</exception>
        /// <exception cref="IncompleteDataException">Raised when source stream was reached end of stream and data is not period terminated.</exception>
        public int ReadPeriodTerminated(Stream storeStream,int maxSize,SizeExceededAction exceededAction)
        {
            if(storeStream == null){
                throw new ArgumentNullException("storeStream");
            }
            lock(this){
                if(m_IsReadActive){
                    throw new InvalidOperationException("There is pending read operation, multiple read operations not allowed !");
                }
                else{
                    m_IsReadActive = true;
                }
            }

            try{
                BufferedStream bufferedStoreStream = new BufferedStream(storeStream,32000);
                bool lineSizeExceeded   = false;
                bool isPeriodTerminated = false;
                int  totalReadedCount   = 0;
                int  readedCount        = 0;
                int  rawReadedCount     = 0;

                // Just break reading at once if maximum allowed line or data size exceeded.
                if(exceededAction == SizeExceededAction.ThrowException){
                    try{
                        // Read first line.
                        readedCount = this.ReadLineInternal(m_pLineBuffer,SizeExceededAction.JunkAndThrowException,out rawReadedCount,false);
                    }
                    catch(LineSizeExceededException x){
                        string dummy = x.Message;
                        lineSizeExceeded = true;
                    }
                    while(rawReadedCount != 0){
                        totalReadedCount += rawReadedCount;

                        // We have data terminator "<CRLF>.<CRLF>".
                        if(readedCount == 1 && m_pLineBuffer[0] == '.'){
                            isPeriodTerminated = true;
                            break;
                        }
                        // If line starts with period(.), first period is removed.
                        else if(m_pLineBuffer[0] == '.'){
                            // Maximum allowed line or data size exceeded.
                            if(lineSizeExceeded || totalReadedCount > maxSize){
                                // Junk data 
                            }
                            // Write readed line to store stream.
                            else{                                
                                bufferedStoreStream.Write(m_pLineBuffer,1,readedCount - 1);
                                bufferedStoreStream.Write(m_LineBreak,0,m_LineBreak.Length);                            
                            }
                        }
                        // Normal line.
                        else{
                            // Maximum allowed line or data size exceeded.
                            if(lineSizeExceeded || totalReadedCount > maxSize){
                                // Junk data                              
                            }
                            // Write readed line to store stream.
                            else{                                
                                bufferedStoreStream.Write(m_pLineBuffer,0,readedCount);
                                bufferedStoreStream.Write(m_LineBreak,0,m_LineBreak.Length);
                            }
                        }                

                        try{
                            // Read next line.
                            readedCount = this.ReadLineInternal(m_pLineBuffer,SizeExceededAction.JunkAndThrowException,out rawReadedCount,false);
                        }
                        catch(LineSizeExceededException x){
                            string dummy = x.Message;
                            lineSizeExceeded = true;
                        }
                    }
                }
                // Read and junk all data if maximum allowed line or data size exceeded.
                else{
                    // Read first line.
                    readedCount = this.ReadLineInternal(m_pLineBuffer,SizeExceededAction.JunkAndThrowException,out rawReadedCount,false);
                    while(rawReadedCount != 0){
                        totalReadedCount += rawReadedCount;

                        // We have data terminator "<CRLF>.<CRLF>".
                        if(readedCount == 1 && m_pLineBuffer[0] == '.'){
                            isPeriodTerminated = true;
                            break;
                        }
                        // If line starts with period(.), first period is removed.
                        else if(m_pLineBuffer[0] == '.'){
                            // Maximum allowed size exceeded.
                            if(totalReadedCount > maxSize){
                                throw new DataSizeExceededException();
                            }
                            
                            // Write readed line to store stream.
                            bufferedStoreStream.Write(m_pLineBuffer,1,readedCount - 1);
                            bufferedStoreStream.Write(m_LineBreak,0,m_LineBreak.Length);                            
                        }
                        // Normal line.
                        else{
                            // Maximum allowed size exceeded.
                            if(totalReadedCount > maxSize){
                                throw new DataSizeExceededException();                                
                            }
                            
                            // Write readed line to store stream.
                            bufferedStoreStream.Write(m_pLineBuffer,0,readedCount);
                            bufferedStoreStream.Write(m_LineBreak,0,m_LineBreak.Length);                            
                        }                

                        // Read next line.
                        readedCount = this.ReadLineInternal(m_pLineBuffer,SizeExceededAction.JunkAndThrowException,out rawReadedCount,false);
                    }
                }
                bufferedStoreStream.Flush();

                // Log
                if(this.Logger != null){
                    this.Logger.AddRead(totalReadedCount,null);
                }

                if(lineSizeExceeded){
                    throw new LineSizeExceededException();
                }
                if(!isPeriodTerminated){
                    throw new IncompleteDataException("Source stream was reached end of stream and data is not period terminated !");
                }
                if(totalReadedCount > maxSize){
                    throw new DataSizeExceededException();
                }

                return totalReadedCount;
            }
            finally{
                m_IsReadActive = false;
            }
        }

        #endregion


        #region method BeginWrite

        /// <summary>
        /// Starts writing specified data to source stream.
        /// </summary>
        /// <param name="data">Data what to write to source stream.</param>
        /// <param name="callback">Callback to be callled if write completes.</param>
        /// <exception cref="ArgumentNullException">Raised when <b>data</b> is null.</exception>
        /// <exception cref="InvalidOperationException">Raised when there already is pending write operation.</exception>
        public void BeginWrite(byte[] data,WriteCallback callback)
        {
            if(data == null){
                throw new ArgumentNullException("data");
            }
            
            BeginWrite(data,0,data.Length,callback);
        }

        /// <summary>
        /// Starts writing specified data to source stream.
        /// </summary>
        /// <param name="data">Data what to write to source stream.</param>
        /// <param name="offset">The zero-based byte offset in buffer at which to begin copying bytes to the source stream.</param>
        /// <param name="count">The number of bytes to be written to the source stream.</param>
        /// <param name="callback">Callback to be callled if write completes.</param>
        /// <exception cref="ArgumentNullException">Raised when <b>data</b> is null.</exception>
        /// <exception cref="InvalidOperationException">Raised when there already is pending write operation.</exception>
        public void BeginWrite(byte[] data,int offset,int count,WriteCallback callback)
        {
            if(data == null){
                throw new ArgumentNullException("data");
            }
            lock(this){
                if(m_IsWriteActive){
                    throw new InvalidOperationException("There is pending write operation, multiple write operations not allowed !");
                }
                m_IsWriteActive = true;
            }
            
            // Start writing data block.
            m_pStream.BeginWrite(
                data,
                offset,
                count,
                new AsyncCallback(this.InternalBeginWriteCallback),
                new object[]{count,callback}
            );
        }

        #region method InternalBeginWriteCallback

        /// <summary>
        /// Is called when BeginWrite(byte[] data,int offset,int count) has completed.
        /// </summary>
        /// <param name="result"></param>
        private void InternalBeginWriteCallback(IAsyncResult result)
        {
            int           count    = (int)((object[])result.AsyncState)[0];
            WriteCallback callback = (WriteCallback)((object[])result.AsyncState)[1];

            try{
                m_pStream.EndWrite(result);

                // Log
                if(this.Logger != null){
                    this.Logger.AddWrite(count,null);
                }

                if(callback != null){
                    callback(new Write_EventArgs(null));
                }
            }
            catch(Exception x){
                if(callback != null){
                    callback(new Write_EventArgs(x));
                }
            }
            finally{
                m_IsWriteActive = false;
            }
        }

        #endregion

        /// <summary>
        /// Strats writing specified amount of data from <b>stream</b> to source stream.
        /// </summary>
        /// <param name="stream">Stream which data to wite to source stream.</param>
        /// <param name="count">Number of bytes read from <b>stream</b> and write to source stream.</param>
        /// <param name="callback">Callback to be called if asynchronous write completes.</param>
        /// <exception cref="ArgumentNullException">Raised when <b>stream</b> is null.</exception>
        /// <exception cref="InvalidOperationException">Raised when there already is pending write operation.</exception>
        public void BeginWrite(Stream stream,int count,WriteStreamCallback callback)
        {
            if(stream == null){
                throw new ArgumentNullException("stream");
            }
            lock(this){
                if(m_IsWriteActive){
                    throw new InvalidOperationException("There is pending write operation, multiple write operations not allowed !");
                }
                m_IsWriteActive = true;
            }

            m_pBeginWrite_Buffer   = new byte[32000];
            m_pBeginWrite_Stream   = stream;
            m_BeginWrite_Count     = count;
            m_pBeginWrite_Callback = callback;
            m_BeginWrite_Readed    = 0;

            // Start reading data block.
            m_pBeginWrite_Stream.BeginRead(
                m_pBeginWrite_Buffer,
                0,
                Math.Min(m_pBeginWrite_Buffer.Length,m_BeginWrite_Count),
                new AsyncCallback(this.InternalBeginWriteStreamCallback),
                null
            );
        }

        #region method InternalBeginWriteStreamCallback

        /// <summary>
        /// This method is called when BeginWrite has readed new data block.
        /// </summary>
        /// <param name="result"></param>
        private void InternalBeginWriteStreamCallback(IAsyncResult result)
        {
            try{
                int readedCount = m_pBeginWrite_Stream.EndRead(result);

                // We have reached end of stream, we din't get so much data as requested.
                if(readedCount == 0){
                    InternalBeginWriteStreamCompleted(new IncompleteDataException("Read stream didn't have so much data than requested !"));
                }
                else{
                    m_BeginWrite_Readed += readedCount;

                    // Write readed bytes to source stream.
                    m_pStream.Write(m_pBeginWrite_Buffer,0,readedCount);

                    // We have readed all requested data.
                    if(m_BeginWrite_Count == m_BeginWrite_Readed){
                        InternalBeginWriteStreamCompleted(null);
                    }
                    // We need read more data.
                    else{
                        // Start reading data block.
                        m_pBeginWrite_Stream.BeginRead(
                            m_pBeginWrite_Buffer,
                            0,
                            Math.Min(m_pBeginWrite_Buffer.Length,m_BeginWrite_Count - m_BeginWrite_Readed),
                            new AsyncCallback(this.InternalBeginWriteStreamCallback),
                            null
                        );
                    }
                }
            }
            catch(Exception x){
                InternalBeginWriteStreamCompleted(x);
            }
        }

        #endregion

        #region method InternalBeginWriteStreamCompleted

        /// <summary>
        /// Is called when BeginWrite has completed.
        /// </summary>
        /// <param name="exception">Exception happened during write or null if operation was successfull.</param>
        private void InternalBeginWriteStreamCompleted(Exception exception)
        {
            // Release write lock.
            m_IsWriteActive = false;

            // Log
            if(this.Logger != null){
                this.Logger.AddWrite(m_BeginWrite_Readed,null);
            }

            if(m_pBeginWrite_Callback != null){
                m_pBeginWrite_Callback(new WriteStream_EventArgs(exception,m_pBeginWrite_Stream,m_BeginWrite_Readed,m_BeginWrite_Readed));
            }
        }

        #endregion

        #endregion

        #region method Write

        /// <summary>
        /// Writes specified buffer data to source stream.
        /// </summary>
        /// <param name="data">Data buffer.</param>
        /// <exception cref="ArgumentNullException">Raised when <b>data</b> is null.</exception>
        /// <exception cref="InvalidOperationException">Raised when there already is pending write operation.</exception>
        public void Write(byte[] data)
        {
            if(data == null){
                throw new ArgumentNullException("data");
            }

            Write(data,0,data.Length);
        }

        /// <summary>
        /// Writes specified buffer data to source stream.
        /// </summary>
        /// <param name="data">Data buffer.</param>
        /// <param name="offset">The zero-based byte offset in buffer at which to begin copying bytes to the source stream.</param>
        /// <param name="count">The number of bytes to be written to the source stream.</param>
        /// <exception cref="ArgumentNullException">Raised when <b>data</b> is null.</exception>
        /// <exception cref="InvalidOperationException">Raised when there already is pending write operation.</exception>
        public void Write(byte[] data,int offset,int count)
        {
            if(data == null){
                throw new ArgumentNullException("data");
            }
            lock(this){
                if(m_IsWriteActive){
                    throw new InvalidOperationException("There is pending write operation, multiple write operations not allowed !");
                }
                m_IsWriteActive = true;
            }

            try{
                m_pStream.Write(data,offset,count);

                // Log
                if(this.Logger != null){
                    this.Logger.AddWrite(count,null);
                }
            }
            finally{
                m_IsWriteActive = false;
            }
        }
                
        /// <summary>
        /// Reads specified amount of data for the specified stream and writes it to source stream.
        /// </summary>
        /// <param name="stream">Stream from where to read data.</param>
        /// <param name="count">Number of bytes to read and write.</param>
        /// <exception cref="ArgumentNullException">Raised when <b>stream</b> is null.</exception>
        /// <exception cref="ArgumentException">Raised when argument <b>count</b> is less than 1.</exception>
        /// <exception cref="InvalidOperationException">Raised when there already is pending write operation.</exception>
        /// <exception cref="IncompleteDataException">Raised <b>stream</b> has reached end of stream and doesn't have so much data as specified by <b>count</b> argument.</exception>
        public void Write(Stream stream,int count)
        {
            if(stream == null){
                throw new ArgumentNullException("stream");
            }
            if(count < 1){
                throw new ArgumentException("Parameter count value must be >= 1 !");
            }
            lock(this){
                if(m_IsWriteActive){
                    throw new InvalidOperationException("There is pending write operation, multiple write operations not allowed !");
                }
                m_IsWriteActive = true;
            }

            try{
                byte[] buffer           = new byte[32000];
                int    totalReadedCount = 0;
                int    readedCount      = 0;
                while(totalReadedCount < count){
                    // Read data block
                    readedCount = stream.Read(buffer,0,Math.Min(buffer.Length,count - totalReadedCount));

                    // We reached end of stream, no more data. That means we didn't get so much data than requested.
                    if(readedCount == 0){
                        throw new IncompleteDataException("Stream reached end of stream before we got requested count of data !");
                    }
                    totalReadedCount += readedCount;

                    // Write readed data to source stream.
                    m_pStream.Write(buffer,0,readedCount);
                }
                m_pStream.Flush();

                // Log
                if(this.Logger != null){
                    this.Logger.AddWrite(count,null);
                }
            }
            finally{
                m_IsWriteActive = false;
            }
        }

        #endregion

        #region method BeginWriteAll

        /// <summary>
        /// Starts writing all <b>stream</b> data to source stream.
        /// </summary>
        /// <param name="stream">Stream which data to write.</param>
        /// <param name="maxSize">Maximum number of bytes to read from <b>stream</b>.</param>
        /// <param name="tag">User data.</param>
        /// <param name="callback">Callback to be called if asynchronous write completes.</param>
        /// <exception cref="ArgumentNullException">Raised when <b>stream</b> is null.</exception>
        /// <exception cref="InvalidOperationException">Raised when there already is pending write operation.</exception>
        public void BeginWriteAll(Stream stream,int maxSize,object tag,WriteStreamCallback callback)
        {
            if(stream == null){
                throw new ArgumentNullException("stream");
            }
            lock(this){
                if(m_IsWriteActive){
                    throw new InvalidOperationException("There is pending write operation, multiple write operations not allowed !");
                }
                m_IsWriteActive = true;
            }

            m_pBeginWriteAll_Buffer   = new byte[32000];
            m_pBeginWriteAll_Stream   = stream;
            m_BeginWriteAll_MaxSize   = maxSize;
            m_pBeginWriteAll_Callback = callback;
            m_BeginWriteAll_Readed    = 0;

            // Start reading data block.
            m_pBeginWriteAll_Stream.BeginRead(
                m_pBeginWriteAll_Buffer,
                0,
                m_pBeginWriteAll_Buffer.Length,
                new AsyncCallback(this.InternalBeginWriteAllCallback),
                null
            );
        }

        #region method InternalBeginWriteAllCallback

        /// <summary>
        /// This method is called when BeginWriteAll has readed new data block.
        /// </summary>
        /// <param name="result"></param>
        private void InternalBeginWriteAllCallback(IAsyncResult result)
        {
            try{
                int readedCount = m_pBeginWriteAll_Stream.EndRead(result);

                // We have reached end of stream, no more data.
                if(readedCount == 0){
                    InternalBeginWriteAllCompleted(null);
                }
                else{
                    m_BeginWriteAll_Readed += readedCount;

                    // Maximum allowed data size exceeded.
                    if(m_BeginWriteAll_Readed > m_BeginWriteAll_MaxSize){
                        throw new DataSizeExceededException();
                    }

                    // Write readed bytes to source stream.
                    m_pStream.Write(m_pBeginWriteAll_Buffer,0,readedCount);
                                        
                    // Start reading next data block.
                    m_pBeginWriteAll_Stream.BeginRead(
                        m_pBeginWriteAll_Buffer,
                        0,
                        m_pBeginWriteAll_Buffer.Length,
                        new AsyncCallback(this.InternalBeginWriteAllCallback),
                        null
                    );
                    return;
                }
            }
            catch(Exception x){
                InternalBeginWriteAllCompleted(x);
            }
        }

        #endregion

        #region method InternalBeginWriteAllCompleted

        /// <summary>
        /// Is called when BeginWriteAll has completed.
        /// </summary>
        /// <param name="exception">Exception happened during write or null if operation was successfull.</param>
        private void InternalBeginWriteAllCompleted(Exception exception)
        {
            // Release write lock.
            m_IsWriteActive = false;

            // Log
            if(this.Logger != null){
                this.Logger.AddWrite(m_BeginWriteAll_Readed,null);
            }

            if(m_pBeginWriteAll_Callback != null){
                m_pBeginWriteAll_Callback(new WriteStream_EventArgs(exception,m_pBeginWriteAll_Stream,m_BeginWriteAll_Readed,m_BeginWriteAll_Readed));
            }
        }

        #endregion

        #endregion

        #region method WriteAll

        /// <summary>
        /// Writes all stream data to source stream.
        /// </summary>
        /// <param name="stream">Stream which data to write.</param>
        /// <returns>Returns number of bytes written to source stream.</returns>
        /// <exception cref="ArgumentNullException">Raised when <b>stream</b> is null.</exception>
        /// <exception cref="InvalidOperationException">Raised when there already is pending write operation.</exception>
        public int WriteAll(Stream stream)
        {
            return WriteAll(stream,int.MaxValue);
        }

        /// <summary>
        /// Writes all stream data to source stream.
        /// </summary>
        /// <param name="stream">Stream which data to write.</param>
        /// <param name="maxSize">Maximum muber of bytes to read from <b>stream</b> and write source stream.</param>
        /// <returns>Returns number of bytes written to source stream.</returns>
        /// <exception cref="ArgumentNullException">Raised when <b>stream</b> is null.</exception>
        /// <exception cref="InvalidOperationException">Raised when there already is pending write operation.</exception>
        /// <exception cref="DataSizeExceededException">Raised when <b>stream</b> stream has more data than specified by <b>maxSize</b>.</exception>
        public int WriteAll(Stream stream,int maxSize)
        {
            if(stream == null){
                throw new ArgumentNullException("stream");
            }
            lock(this){
                if(m_IsWriteActive){
                    throw new InvalidOperationException("There is pending write operation, multiple write operations not allowed !");
                }
                m_IsWriteActive = true;
            }

            try{
                byte[] buffer           = new byte[32000];
                int    totalReadedCount = 0;
                int    readedCount      = 0;
                while(readedCount > 0){
                    // Read data block
                    readedCount = stream.Read(buffer,0,buffer.Length);

                    // We reached end of stream, no more data.
                    if(readedCount == 0){
                        break;
                    }
                    // We have exceeded maximum allowed data size.
                    else if((totalReadedCount + readedCount) > maxSize){
                        throw new DataSizeExceededException();
                    }
                    totalReadedCount += readedCount;

                    // Write readed data to source stream.
                    m_pStream.Write(buffer,0,readedCount);
                }

                // Log
                if(this.Logger != null){
                    this.Logger.AddWrite(totalReadedCount,null);
                }

                return totalReadedCount;
            }
            finally{
                m_IsWriteActive = false;
            }
        }

        #endregion

        #region method BeginWritePeriodTerminated

        /// <summary>
        /// Starts writing <b>stream</b> data to source stream. Data will be period handled and terminated as needed.
        /// </summary>
        /// <param name="stream">Stream which data to write to source stream.</param>
        /// <param name="maxSize">Maximum muber of bytes to read from <b>stream</b> and write source stream.</param>
        /// <param name="tag">User data.</param>
        /// <param name="callback">Callback to be called if asynchronous write completes.</param>
        /// <exception cref="ArgumentNullException">Raised when <b>stream</b> is null.</exception>
        /// <exception cref="InvalidOperationException">Raised when there already is pending write operation.</exception>
        public void BeginWritePeriodTerminated(Stream stream,int maxSize,object tag,WriteStreamCallback callback)
        {
            if(stream == null){
                throw new ArgumentNullException("stream");
            }
            lock(this){
                if(m_IsWriteActive){
                    throw new InvalidOperationException("There is pending write operation, multiple write operations not allowed !");
                }
                m_IsWriteActive = true;
            }

            m_pBeginWritePeriodTerminated_Stream         = stream;
            m_BeginWritePeriodTerminated_MaxSize         = maxSize;
            m_pBeginWritePeriodTerminated_Callback       = callback;
            m_pBeginWritePeriodTerminated_BufferedStream = new BufferedStream(m_pStream);
        }

        #region method InternalBeginWritePeriodTerminatedReadLineCompleted

        /// <summary>
        /// Is called when BeginWritePeriodTerminated stream.BeginReadLine has completed.
        /// </summary>
        /// <param name="e">Callback data.</param>
        private void InternalBeginWritePeriodTerminatedReadLineCompleted(ReadLine_EventArgs e)
        {
            try{
                // Error happened during read.
                if(e.Exception != null){
                    InternalBeginWritePeriodTerminatedCompleted(e.Exception);
                }
                // We reached end of stream, no more data.
                else if(e.ReadedCount == 0){
                    InternalBeginWritePeriodTerminatedCompleted(null);
                }
                else{
                    m_BeginWritePeriodTerminated_Readed += e.ReadedCount;

                    // Maximum allowed data size exceeded.
                    if(m_BeginWritePeriodTerminated_Readed > m_BeginWritePeriodTerminated_MaxSize){
                        throw new DataSizeExceededException();
                    }

                    // If line starts with period, addtional period is added.
                    if(e.LineBuffer[0] == '.'){
                        m_pBeginWritePeriodTerminated_BufferedStream.WriteByte((int)'.');
                        m_BeginWritePeriodTerminated_Written++;
                    }

                    // Write readed line to buffered stream.
                    m_pBeginWritePeriodTerminated_BufferedStream.Write(e.LineBuffer,0,e.Count);                    
                    m_pBeginWritePeriodTerminated_BufferedStream.Write(m_LineBreak,0,m_LineBreak.Length);
                    m_BeginWritePeriodTerminated_Written += e.Count + m_LineBreak.Length;
                }
            }
            catch(Exception x){
                InternalBeginWritePeriodTerminatedCompleted(x);
            }
        }

        #endregion

        #region method InternalBeginWritePeriodTerminatedCompleted

        /// <summary>
        /// Is called when asynchronous write period terminated has completed.
        /// </summary>
        /// <param name="exception">Exception happened during write or null if operation was successfull.</param>
        private void InternalBeginWritePeriodTerminatedCompleted(Exception exception)
        {
            // Release write lock.
            m_IsWriteActive = false;

            // Force to write buffer to source stream, if any.
            try{
                m_pBeginWritePeriodTerminated_BufferedStream.Flush();
            }
            catch{
            }

            // Log
            if(this.Logger != null){
                this.Logger.AddWrite(m_BeginWritePeriodTerminated_Written,null);
            }

            if(m_pBeginWritePeriodTerminated_Callback != null){
                m_pBeginWritePeriodTerminated_Callback(new WriteStream_EventArgs(exception,m_pBeginWritePeriodTerminated_Stream,m_BeginWritePeriodTerminated_Readed,m_BeginWritePeriodTerminated_Written));
            }
        }

        #endregion

        #endregion

        #region method WritePeriodTerminated

        /// <summary>
        /// Reades all data from the specified stream and writes it to source stream. Period handlign and period terminator is added as required.
        /// </summary>
        /// <param name="stream">Stream which data to write to source stream.</param>
        /// <returns>Returns number of bytes written to source stream. Note this value differs from 
        /// <b>stream</b> readed bytes count because of period handling and period terminator.
        /// </returns>
        /// <exception cref="ArgumentNullException">Raised when <b>stream</b> is null.</exception>
        /// <exception cref="InvalidOperationException">Raised when there already is pending write operation.</exception>
        /// <exception cref="LineSizeExceededException">Raised when <b>stream</b> contains line with bigger line size than allowed.</exception>
        /// <exception cref="DataSizeExceededException">Raised when <b>stream</b> has more data than <b>maxSize</b> allows..</exception>
        public int WritePeriodTerminated(Stream stream)
        {
            return WritePeriodTerminated(stream,int.MaxValue);
        }

        /// <summary>
        /// Reades all data from the specified stream and writes it to source stream. Period handlign and period terminator is added as required.
        /// </summary>
        /// <param name="stream">Stream which data to write to source stream.</param>
        /// <param name="maxSize">Maximum muber of bytes to read from <b>stream</b> and write source stream.</param>
        /// <returns>Returns number of bytes written to source stream. Note this value differs from 
        /// <b>stream</b> readed bytes count because of period handling and period terminator.
        /// </returns>
        /// <exception cref="ArgumentNullException">Raised when <b>stream</b> is null.</exception>
        /// <exception cref="InvalidOperationException">Raised when there already is pending write operation.</exception>
        /// <exception cref="LineSizeExceededException">Raised when <b>stream</b> contains line with bigger line size than allowed.</exception>
        /// <exception cref="DataSizeExceededException">Raised when <b>stream</b> has more data than <b>maxSize</b> allows..</exception>
        public int WritePeriodTerminated(Stream stream,int maxSize)
        {
            if(stream == null){
                throw new ArgumentNullException("stream");
            }
            lock(this){
                if(m_IsWriteActive){
                    throw new InvalidOperationException("There is pending write operation, multiple write operations not allowed !");
                }
                m_IsWriteActive = true;
            }

            try{                
                BufferedStream bufferedStoreStream = new BufferedStream(m_pStream,32000);
                StreamHelper   reader              = new StreamHelper(stream);
                int            totalWrittenCount   = 0;
                int            readedCount         = 0;
                int            rawReadedCount      = 0;
                while(true){
                    // Read data block.
                    readedCount = this.ReadLineInternal(m_pLineBuffer,SizeExceededAction.ThrowException,out rawReadedCount,false);

                    // We reached end of stream, no more data.
                    if(readedCount == 0){
                        break;
                    }

                    // Maximum allowed data size exceeded.
                    if((totalWrittenCount + rawReadedCount) > maxSize){
                        throw new DataSizeExceededException();
                    }

                    // If line starts with period(.), additional period is added.
                    if(m_pLineBuffer[0] == '.'){
                        bufferedStoreStream.WriteByte((byte)'.');
                        totalWrittenCount++;
                    }

                    // Write readed line to buffered stream.
                    bufferedStoreStream.Write(m_pLineBuffer,0,readedCount);
                    bufferedStoreStream.Write(m_LineBreak,0,m_LineBreak.Length);
                    totalWrittenCount += (readedCount + m_LineBreak.Length);
                }
                                
                // Write terminator ".<CRLF>". We have start <CRLF> already in stream.
                bufferedStoreStream.Write(new byte[]{(byte)'.',(byte)'\r',(byte)'\n'},0,3);
                bufferedStoreStream.Flush();
                m_pStream.Flush();

                // Log
                if(this.Logger != null){
                    this.Logger.AddWrite(totalWrittenCount,null);
                }

                return totalWrittenCount;
            }
            finally{
                m_IsWriteActive = false;
            }
        }

        #endregion


        #region Async read line

        #region method OnReadByte_Completed

        /// <summary>
        /// Is called when asynchronous read byte operation has completed.
        /// </summary>
        /// <param name="result"></param>
        private void OnReadByte_Completed(IAsyncResult result)
        {
            try{
                int readedCount = m_pStream.EndRead(result);
                if(readedCount == 1){
                    m_RLine_TotalReadedCount++;

                    // We have LF.
                    if(m_pRLine_ByteBuffer[0] == '\n'){
                        // Line size eceeded and some data junked.
                        if(m_RLine_LineBufferOffset > m_RLine_LineBufferSize){
                            OnReadLineCompleted(new LineSizeExceededException());
                        }
                        // Read line completed sucessfully.
                        else{
                            OnReadLineCompleted(null);
                        }
                        return;
                    }
                    // We just skip all CR.
                    else if(m_pRLine_ByteBuffer[0] == '\r'){
                    }
                    else{
                        // Maximum allowed line size exceeded.
                        if(m_RLine_LineBufferOffset >= m_RLine_LineBufferSize){
                            if(m_RLine_ExceedAction == SizeExceededAction.ThrowException){
                                OnReadLineCompleted(new LineSizeExceededException());
                                return;
                            }
                        }
                        // Write readed byte to line buffer.
                        else{
                            m_pRLine_LineBuffer[m_RLine_LineBufferOffset] = m_pRLine_ByteBuffer[0];
                            m_RLine_LineBufferOffset++;
                        }
                    }

                    // Get next byte.
                    m_pStream.BeginRead(m_pRLine_ByteBuffer,0,1,new AsyncCallback(this.OnReadByte_Completed),null);
                }
                // We have no more data.
                else{
                    // Line size eceeded and some data junked.
                    if(m_RLine_LineBufferOffset >= m_RLine_LineBufferSize){
                        OnReadLineCompleted(new LineSizeExceededException());
                    }
                    // Read line completed sucessfully.
                    else{
                        OnReadLineCompleted(null);
                    }
                }
            }
            catch(Exception x){
                OnReadLineCompleted(x);
            }
        }

        #endregion

        #region method DoReadLine_Buffered

        /// <summary>
        /// Tries to read line from data buffer, if no line in buffer, new data buffer will be readed(buffered).
        /// </summary>
        private void DoReadLine_Buffered()
        {
            try{
                byte currentByte = 0;
                while(m_ReadBufferOffset < m_ReadBufferEndPos){                        
                    currentByte = m_pReadBuffer[m_ReadBufferOffset];
                    m_ReadBufferOffset++;
                    m_RLine_TotalReadedCount++;

                    // We have LF.
                    if(currentByte == '\n'){
                        // Line size eceeded and some data junked.
                        if(m_RLine_TotalReadedCount > m_RLine_LineBufferSize){
                            OnReadLineCompleted(new LineSizeExceededException());
                        }
                        // Read line completed sucessfully.
                        else{
                            OnReadLineCompleted(null);
                        }
                        return;
                    }
                    // We just skip all CR.
                    else if(currentByte == '\r'){
                    }
                    else{
                        // Maximum allowed line size exceeded.
                        if(m_RLine_TotalReadedCount >= m_RLine_LineBufferSize){
                            if(m_RLine_ExceedAction == SizeExceededAction.ThrowException){
                                OnReadLineCompleted(new LineSizeExceededException());
                                return;
                            }
                        }
                        // Write readed byte to line buffer.
                        else{                                
                            m_pRLine_LineBuffer[m_RLine_LineBufferOffset] = currentByte;
                            m_RLine_LineBufferOffset++;
                        }
                    }
                }
                    
                // If we reach here, that means we consumed all read buffer and no line was in it.
                // Just get new data buffer block.
                m_pStream.BeginRead(m_pReadBuffer,0,m_pReadBuffer.Length,new AsyncCallback(this.OnReadBuffer_Completed),null);
            }
            catch(Exception x){
                OnReadLineCompleted(x);
            }
        }

        #endregion

        #region mehtod OnReadBuffer_Completed

        /// <summary>
        /// Is called when asynchronous data buffering has completed.
        /// </summary>
        /// <param name="result"></param>
        private void OnReadBuffer_Completed(IAsyncResult result)
        {
            try{                    
                m_ReadBufferEndPos = m_pStream.EndRead(result);
                m_ReadBufferOffset = 0;

                // We have reached end of stream, no more data.
                if(m_ReadBufferEndPos == 0){
                    // Line size eceeded and some data junked.
                    if(m_RLine_TotalReadedCount > m_RLine_LineBufferSize){
                        OnReadLineCompleted(new LineSizeExceededException());
                    }
                    // Read line completed sucessfully.
                    else{
                        OnReadLineCompleted(null);
                    }
                }
                // Continue line reading.
                else{                        
                    DoReadLine_Buffered();
                }
            }
            catch(Exception x){
                OnReadLineCompleted(x);
            }
        }

        #endregion


        #region mehtod OnReadLineCompleted

        /// <summary>
        /// Is called when read line has completed.
        /// </summary>
        /// <param name="x">Excheption what happened during line reading or null if read line was completed sucessfully.</param>
        private void OnReadLineCompleted(Exception x)
        {
            if(m_RLine_UnlockRead){
                // Release reader lock.
                m_IsReadActive = false;
            }

            if(m_RLine_Log && this.Logger != null){
                this.Logger.AddRead(m_RLine_TotalReadedCount,null);
            }

            if(m_pRLine_Callback != null){
                m_pRLine_EventArgs.Reuse(x,m_RLine_TotalReadedCount,m_pRLine_LineBuffer,m_RLine_LineBufferOffset,m_pRLine_Tag);
                m_pRLine_Callback(m_pRLine_EventArgs);
            }
        }

        #endregion

        #endregion


        #region Properties Implementation

        /// <summary>
        /// Gets underlying stream.
        /// </summary>
        public Stream Stream
        {
            get{ return m_pStream; }
        }

        /// <summary>
        /// Gets maximum allowed line size in bytes.
        /// </summary>
        public int MaximumLineSize
        {
            get{ return m_MaxLineSize; }
        }

        /// <summary>
        /// Gets if source stream reads are buffered.
        /// </summary>
        public bool IsReadBuffered
        {
            get{ return m_IsReadBuffered; }
        }

        /// <summary>
        /// Gets or sets logger to use for logging.
        /// </summary>
        public Logger Logger
        {
            get{ return m_pLogger; }

            set{ m_pLogger = value; }
        }

        #endregion

    }
}
